/* browser.c
 *
 * Copyright 2007-2008 OpenMoko, Inc.
 * Authored by Tick <tick@openmoko.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#define _GNU_SOURCE

#include <unistd.h>
#include <browser.h>
#include <Ecore_Evas.h>
#include <etk/Etk.h>
#include <debug.h>
#include <packagekit/pk-package-id.h>
#include <string.h>
#include <assassin.h>
#include <gears.h>
#include <confirm_command.h>

extern  Ecore_Evas  *   ee;
Evas_List *category_list = NULL;
enum ActionType get_actiontype();
static Etk_Bool categories_selected(Etk_Object *object, Etk_Tree_Row *row, Etk_Event_Mouse_Up *event, void *data);
static Etk_Bool packages_selected(Etk_Object *object, Etk_Tree_Row *row, Etk_Event_Mouse_Up *event, void *data) ;

struct search_iter {
    int search_update;
    PkGroupEnum group;
    struct Group_List *GL;
};

static void __search_add(struct search_iter *);
static void __search_action();
static void search_group(PkGroupEnum group, struct Group_List *GL) ;
static int search_updates(void *data) ;
void pkg_item_free(struct PKG_Item *item);

// It's a little bit hacking here.
// remove the categories list to a particular file
#include "categories_list.txt"

static int get_categories_lists (void * data) {
    struct Scroll_List *SL = (struct Scroll_List *) data;
    int i = 0;
    struct Group_List * GL;
    struct search_iter *iter;
    //ETK_TREE_ROW_CLICKED_SIGNAL
    if (category_list) {
        error("This function shall only run once!\n");
        return 0;
    }
    put_a_dim_on_screen();
    for (i = 0;CATEGORIES[i].title;i++) {
        GL = get_packages_list(CATEGORIES[i].title, CATEGORIES[i].group, CATEGORIES[i].GL);
        CATEGORIES[i].GL = GL;
        category_list = evas_list_append(category_list, GL);
        GL->row = scroll_list_append(SL, CATEGORIES[i].title);
    }
    etk_signal_connect_by_code(ETK_TREE_ROW_CLICKED_SIGNAL, ETK_OBJECT(SL->tree), &categories_selected, category_list);
    //Get Update packages

    iter = (struct search_iter*)calloc(1, sizeof(struct search_iter));
    iter->search_update = 1;
    iter->GL = evas_list_nth(category_list, 0);
    __search_add(iter);

    __search_action();
    return 0;
}

void refresh_categories_lists(void) {
    int i, j;
    struct Group_List * GL;
    struct PKG_Item *item;
    struct search_iter *iter;

    if (!category_list) {
        error("category_list should be there!!\n");
        return;
    }
    sync();
    sync();
    for (i = 0;i < evas_list_count(category_list);i++) {
        GL = evas_list_nth(category_list, i);
        for (j = 0;j < NUMBER_BUTTOM_LIST;j++) {
            if (evas_list_count(GL->sublist[j])) {
                etk_signal_disconnect_by_code(ETK_TREE_ROW_CLICKED_SIGNAL, ETK_OBJECT(GL->list[j]->tree), &packages_selected, GL->sublist[j]);
            }
            while (GL->sublist[j] && evas_list_count(GL->sublist[j]) >= 1) {
                item = evas_list_nth(GL->sublist[j], 0);
                if (item == NULL) {
                    error("item is NULL\n");
                    break;
                }
                GL->sublist[j] = evas_list_remove(GL->sublist[j], item);
                pkg_item_free(item);
                item = NULL;
            }
            evas_list_free(GL->sublist[j]);
            GL->sublist[j] = NULL;
            scroll_list_clear(GL->list[j]);
        }
        iter = (struct search_iter *)calloc(1, sizeof(struct search_iter));
        iter->group = CATEGORIES[i].group;
        iter->GL = GL;
        __search_add(iter);
    }
    //Get Update packages
    iter = (struct search_iter*)calloc(1, sizeof(struct search_iter));
    iter->search_update = 1;
    iter->GL = evas_list_nth(category_list, 0);
    __search_add(iter);
    __search_action();
}

struct Scroll_Page * get_categories_page () {
    static struct Scroll_Page * SP = NULL;
    struct Scroll_List * SL;
    if (SP) {
        //TODO Put Update hacking here
        if (get_button_bar_selected_type() != UPDATE) {
            return SP;
        } else {
            
            return category_list ? ((struct Group_List *)evas_list_nth(category_list, 0))->page[UPDATE]: NULL;
        }
    }
    SP = scroll_page_new();
    SL = scroll_list_new(SP);

    scroll_page_set_title(SP, "Categories");
    etk_widget_show_all(SL->embed);
    //get_categories_lists(SL);
    ecore_timer_add(FINISH_TIME_MAGIC, &get_categories_lists, SL);;
    current_scroll_page_set(SP);
    return SP;
}


inline int get_GL_index(char *info) {
    const char * AVAILIABLE = "available";
    const char * INSTALLED = "installed";
    const int INDEX_INSTALL = 0;
    const int INDEX_UPDATE = 1;
    const int INDEX_UNINSTALL = 2;

    if (!info) return -1;
    if (!strncmp(info, AVAILIABLE, strlen(AVAILIABLE))) {
        return INDEX_INSTALL;
    } else if (!strncmp(info, INSTALLED, strlen(INSTALLED))) {
        return INDEX_UNINSTALL;
    } else if (!strncmp(info, "low", strlen("low")) ||
               !strncmp(info, "enhancement", strlen("enhancement"))) {
        return INDEX_UPDATE;
    }
    return -1;
}

static void package_detail_delete (void *data) {
    struct Package_Detail *package_detail = (struct Package_Detail *) data;
    if (!data) {
        error("data shall not be null!!\n");
        return;
    }
    debug("Try to free package detail.\n");
    evas_object_hide(package_detail->package_view);
    etk_widget_hide(package_detail->etk_evas_obj);
    etk_widget_hide(package_detail->scrolled_view);
    etk_widget_hide(package_detail->embed);
    evas_object_hide(package_detail->description);
    evas_object_hide(package_detail->thumbnail);
    etk_object_destroy(ETK_OBJECT(package_detail->scrolled_view));
    etk_object_destroy(ETK_OBJECT(package_detail->embed));
    etk_object_destroy(ETK_OBJECT(package_detail->etk_evas_obj));
    evas_object_del(package_detail->description);
    evas_object_del(package_detail->thumbnail);
    evas_object_del(package_detail->package_view);
    free(package_detail->packageid);
    free(package_detail->alias);
    free(package_detail);
}

inline struct Target * packageid_to_target (char *packageid) {
    struct Target * T =  target_new();
    T->type = get_button_bar_selected_type();
    T->status = INITIAL;
    T->package = packageid;
    return T;
}

// Using this __need_refresh_after_action__ is because there are no way to get the refresh able info from packagekid, it was not designed for that.
// FIXME: it's ugly
static int __need_refresh_after_action__ = 0;
static void package_action (void *data,  Evas_Object *obj, const char *emission, const char *source) {
    struct Package_Detail *package_detail = (struct Package_Detail *)data;
    struct Target * T = packageid_to_target(package_detail->packageid);
    T->need_refresh =  __need_refresh_after_action__ ;
    T->alias = strdup(package_detail->alias);
    debug("Try to confirm %d %s\n", T->type, T->package);
    do_confirm(ecore_evas_get(ee), T);
}

static void pk_get_details_cb (void *data, DBusMessage *message) {
    Pk_Operation * pko = (Pk_Operation *)data;
    struct Scroll_Page *SP;
    DBusError _err;
    int ret;
    char *packageid;
    char *license;
    char *group_text;
    char *description;
    char *url;
    unsigned int size;
    pko_timeout_check(pko);

    if (!data) {
        error("data shall not be null!!\n");
        return;
    }
    dbus_error_init(&_err);
    ret = dbus_message_get_args(message, &_err,
                                DBUS_TYPE_STRING, &packageid,
                                DBUS_TYPE_STRING, &license,
                                DBUS_TYPE_STRING, &group_text,
                                DBUS_TYPE_STRING, &description,
                                DBUS_TYPE_STRING, &url,
                                DBUS_TYPE_UINT64, &size,
                                DBUS_TYPE_INVALID);
    if (ret == 0) {
        error("Error message: %s\n", _err.message);
        dbus_error_free(&_err);
        return;
    }
    dbus_error_free(&_err);
    debug("\n");
    SP = get_package_detail_sp(pko, packageid, group_text, description, size);
    scroll_page_shows(SP);
}

static const char* __remap_the_group_text(const char *group_text) {
    int i;
    for (i = 0;CATEGORIES[i].remap;i++) {
        if (!strcmp(CATEGORIES[i].remap, group_text))
            return CATEGORIES[i].title;
    }
    return group_text;
}

struct Scroll_Page *get_package_detail_sp(Pk_Operation * pko, char *packageid, char * group_text, char * description, int size) {
    struct Package_Detail *package_detail;
    Evas_Object *_pv;
    struct Scroll_Page *SP;
    char buf[MAX_PACKAGE_NAME_SIZE];
    char name[MAX_PACKAGE_NAME_SIZE];
    int desc_w, desc_h;
    int x, y, w, h;

    // To decide if need to refresh after action
    // FIXME: It's ugly
    __need_refresh_after_action__ = strcmp(group_text, "repos") ? !!0 : !!1;

    package_detail = (struct Package_Detail *) malloc(sizeof(struct Package_Detail));
    SP = scroll_page_new();
    scroll_page_set_volatilize(SP, 1);
    _pv = new_package_view(ecore_evas_get(ee), packageid);
    package_detail->package_view = _pv;
    edje_object_part_swallow(SP->frame, "scroll.frame", _pv );

    get_pk_name_from_id(packageid, description, name, sizeof(name));
    package_detail->packageid = strdup(packageid);
    package_detail->alias = strdup(name);
    edje_object_part_text_set(_pv , "package.name.text", name);
    snprintf(buf, sizeof(buf), "Categories / %s / %s", __remap_the_group_text(group_text), name);
    debug("get %s\n", buf);
    scroll_page_set_title(SP, buf);


    package_detail->etk_evas_obj = etk_evas_object_new();
    package_detail->scrolled_view = etk_scrolled_view_new();
    etk_scrolled_view_policy_set(ETK_SCROLLED_VIEW(package_detail->scrolled_view), ETK_POLICY_HIDE, ETK_POLICY_HIDE);
    etk_scrolled_view_dragable_set(ETK_SCROLLED_VIEW(package_detail->scrolled_view), ETK_TRUE);
    package_detail->embed = etk_embed_new(ecore_evas_get(ee));
    etk_scrolled_view_add_with_viewport(ETK_SCROLLED_VIEW(package_detail->scrolled_view), ETK_WIDGET(package_detail->etk_evas_obj));
    etk_container_add(ETK_CONTAINER(package_detail->embed), package_detail->scrolled_view);
    edje_object_part_swallow(_pv, "package.description.swallow",
                             etk_embed_object_get(ETK_EMBED(package_detail->embed)));

    package_detail->description = assassin_edje_object_get("package.description");
    evas_object_geometry_get(
        edje_object_part_object_get(_pv, "package.description.swallow"),
        NULL, NULL, &desc_w, &desc_h);
    debug("package_detail->package_view size %d %d\n", desc_w, desc_h);

    if (strstr(description, " ;; ")) {
        description = strstr(description, " ;; ") + strlen(" ;; "); // For alias
    }
    edje_object_part_text_set(package_detail->description, "package.description.textblock", description);
    etk_evas_object_set_object(ETK_EVAS_OBJECT(package_detail->etk_evas_obj), package_detail->description);
    edje_object_size_min_restricted_calc(package_detail->description, &w, &h, desc_w, 0);
    etk_widget_size_request_set(ETK_WIDGET(package_detail->etk_evas_obj), w, h);
    debug("package_detail->description %d %d\n", w, h);
    etk_widget_size_request_set(ETK_WIDGET(package_detail->scrolled_view), desc_w, desc_h);


    // size
    if (size <= 0) {
        snprintf(buf, sizeof(buf), "Download size: ? Bytes");
    } else if (size > 1024) {
        snprintf(buf, sizeof(buf), "Download size: %d KB", (int)size / 1024);
    } else {
        snprintf(buf, sizeof(buf), "Download size: %d Bytes", (int)size);
    }
    edje_object_part_text_set(_pv , "package.size.text", buf);

    // Thumbnail text bar
    if (get_button_bar_selected_type() == INSTALL) {
        snprintf(buf, sizeof(buf), "(Touch me to install)");
    } else if (get_button_bar_selected_type() == UPDATE) {
        snprintf(buf, sizeof(buf), "(Touch me to update)");
    } else {
        snprintf(buf, sizeof(buf), "(Touch me to uninstall)");
    }
    edje_object_part_text_set(_pv, "touchme.label.text", buf);

    get_pk_name_from_id(packageid, NULL, buf, sizeof(buf));
    package_detail->thumbnail = get_thumbnail_from_eet(group_text, buf);
    if (!package_detail->thumbnail) {
        package_detail->thumbnail = assassin_edje_object_get("default.thumbnail");
    }
    edje_object_part_swallow(_pv, "thumbnail.swallow", package_detail->thumbnail);
    evas_object_geometry_get(package_detail->thumbnail, &x, &y, &w, &h);
    debug("The position of thumbnail.swallow is %d %d size %d %d\n", x, y, w, h);
    evas_object_image_fill_set(package_detail->thumbnail, 0, 0, w, h);

    SP->embed = package_detail;
    SP->free_embed = & package_detail_delete;
    edje_object_signal_callback_add(_pv, "mouse,clicked,1", "thumbnail.swallow", &package_action, package_detail );
    evas_object_move(SP->frame, WIDTH(), 0);
    etk_widget_show_all(ETK_WIDGET(package_detail->embed));
    etk_widget_show_all(ETK_WIDGET(package_detail->scrolled_view));
    evas_object_show(package_detail->description);
    evas_object_show( _pv );
    return SP;

}

#define LENGTH_NAME_SHOWED 64
static void pk_default_search_group_cb (void *data, DBusMessage *message) {
    DBusError _err;
    char * info;
    char * packageid;
    char * summary;
    int ret;
    Pk_Operation * pko = (Pk_Operation *)data;
    struct Group_List * GL = (struct Group_List *)pko->payload;
    struct PKG_Item *pkg_item = NULL;
    char name[LENGTH_NAME_SHOWED];

    pko_timeout_check(pko);

    dbus_error_init(&_err);
    ret = dbus_message_get_args(message, &_err,
                                DBUS_TYPE_STRING, &info,
                                DBUS_TYPE_STRING, &packageid,
                                DBUS_TYPE_STRING, &summary,
                                DBUS_TYPE_INVALID);
    if (!ret) {
        error("error message: '%s'\n", _err.message);
        dbus_error_free(&_err);
        return;
    }
    dbus_error_free(&_err);
    if (GL == NULL) {
        error("GL shall not be Null now!!\n");
        return;
    }
    ret = get_GL_index(info);
    if (ret < 0) {
        debug("info of package: '%s' is '%s' and assassin do not know that!\n", packageid, info);
        return;
    }
    get_pk_name_from_id(packageid, summary, name, sizeof(name));
    pkg_item = (struct PKG_Item *)malloc(sizeof(struct PKG_Item));

    pkg_item->packageid = strdup(packageid);
    pkg_item->row = scroll_list_append(GL->list[ret], name);
    GL->sublist[ret] = evas_list_append(GL->sublist[ret], pkg_item);
    if (evas_list_count(GL->sublist[ret]) == 1) {
        etk_signal_connect_by_code(ETK_TREE_ROW_CLICKED_SIGNAL, ETK_OBJECT(GL->list[ret]->tree), &packages_selected, GL->sublist[ret]);
    }
}

static void pk_default_search_updates_cb (void *data, DBusMessage *message) {
    DBusError _err;
    char * info;
    char * packageid;
    char * summary;
    int ret;
    Pk_Operation * pko = (Pk_Operation *)data;
    struct Group_List * GL = (struct Group_List *)pko->payload;
    struct PKG_Item *pkg_item = NULL;
    char name[LENGTH_NAME_SHOWED];

    pko_timeout_check(pko);

    dbus_error_init(&_err);
    ret = dbus_message_get_args(message, &_err,
                                DBUS_TYPE_STRING, &info,
                                DBUS_TYPE_STRING, &packageid,
                                DBUS_TYPE_STRING, &summary,
                                DBUS_TYPE_INVALID);
    if (!ret) {
        error("error message: '%s'\n", _err.message);
        dbus_error_free(&_err);
        return;
    }
    dbus_error_free(&_err);
    if (GL == NULL) {
        error("GL shall not be Null now!!\n");
        return;
    }
    ret = UPDATE;
    if (ret < 0) {
        debug("info of package: '%s' is '%s' and assassin do not know that!\n", packageid, info);
        return;
    }
    if (!strstr(summary, "group::")) { // Only visibile package will appears
        remove_the_dim_on_screen();
        return;
    }
    get_pk_name_from_id(packageid, summary, name, sizeof(name));
    pkg_item = (struct PKG_Item *)malloc(sizeof(struct PKG_Item));

    pkg_item->packageid = strdup(packageid);
    pkg_item->row = scroll_list_append(GL->list[ret], name);
    GL->sublist[ret] = evas_list_append(GL->sublist[ret], pkg_item);
    if (evas_list_count(GL->sublist[ret]) == 2) { // first one is "UPDATE ALL"
        etk_signal_connect_by_code(ETK_TREE_ROW_CLICKED_SIGNAL, ETK_OBJECT(GL->list[ret]->tree), &packages_selected, GL->sublist[ret]);
    }
    remove_the_dim_on_screen();
}

static void pk_search_update_error_cb (void *data, DBusMessage *message) {
    char *_message;
    char *detail;
    Pk_Operation * pko = (Pk_Operation *)data;
    DBusError _err;

    pko_timeout_check(pko);
    dbus_error_init(&_err);
    if (!dbus_message_get_args(message, &_err,
                               DBUS_TYPE_STRING, &_message,
                               DBUS_TYPE_STRING, &detail,
                               DBUS_TYPE_INVALID
                              )) {
        error("Trans: %s Error Message: '%s'\n", pko->tid, _err.message);
        dbus_error_free(&_err);
        return;
    }
    dbus_error_free(&_err);
    debug("Trans: %s Error %s '%s'\n", pko->tid, _message, detail);
}


static void pk_query_finished_cb (void *data, DBusMessage *message) {
    Pk_Operation * pko = (Pk_Operation *)data;
    struct Group_List * GL = (struct Group_List *)pko->payload;
    struct PKG_Item *pkg_item = NULL;
    const int APPEND_LAST_ROW_MAGIC = 8;
    const char *ENDOFLIST="ASSASSIN_END_OF_LIST";
    int list=0;
    unsigned int count=0;
    for (list=0;list < NUMBER_BUTTOM_LIST; list ++) {
        count = evas_list_count(GL->sublist[list]);
        if (count > APPEND_LAST_ROW_MAGIC) {
            pkg_item = (struct PKG_Item *) evas_list_nth(GL->sublist[list], (count - 1));
            if (strcmp(pkg_item->packageid, ENDOFLIST)) {
                pkg_item  = (struct PKG_Item *)malloc(sizeof(struct PKG_Item));
                pkg_item->packageid = strdup(ENDOFLIST);
                pkg_item->row = scroll_list_append(GL->list[list], "");
                GL->sublist[list] = evas_list_append(GL->sublist[list], pkg_item);
            }
        }
    }
    remove_the_dim_on_screen();
    pk_default_get_finished_cb(data, message);
    __search_action();
}

static Evas_List *__search_list = NULL;
static void __search_add(struct search_iter *iter) {
    if (!iter)
        return;
    __search_list = evas_list_append(__search_list, iter);
}
static void __search_action() {
    struct search_iter *iter = evas_list_nth(__search_list, 0);
    if (!iter)
        return;
    __search_list = evas_list_remove(__search_list, iter);
    if (iter->search_update) {
        search_updates(iter->GL);
    } else {
        search_group(iter->group, iter->GL);
    }
    free(iter);
}

static void search_group(PkGroupEnum group, struct Group_List *GL) {
    Pk_Operation *  pko = pko_new();
    debug("Search group %d %s\n", group, pk_group_enum_to_text(group));
    pko->Operations[Package_sig]   = &pk_default_search_group_cb;
    pko->Operations[ErrorCode_sig] = &pk_search_update_error_cb;
    pko->Operations[Finished_sig]  = &pk_query_finished_cb;
    pko->payload = GL;
    pk_search_group(pko, pk_group_enum_to_text(group));
}

static int search_updates(void *data) {
    struct Group_List *GL = (struct Group_List *)data;
    //insert_update_all(GL);
    Pk_Operation *  pko = pko_new();
    debug("Search update\n");
    pko->Operations[Package_sig] = &pk_default_search_updates_cb;
    pko->Operations[ErrorCode_sig] = &pk_search_update_error_cb;
    pko->Operations[Finished_sig]  = &pk_query_finished_cb;
    pko->payload = GL;
    pk_get_updates(pko);
    return 0;
}

struct Group_List * get_packages_list(const char *header, PkGroupEnum group, struct Group_List *GL) {
    char buf[MAX_PACKAGE_NAME_SIZE];
    static struct Scroll_Page *__update_sigle = NULL;
    static struct Scroll_List *__update_list = NULL;
    struct search_iter *iter = NULL;
    int i;
    if (GL == NULL) {
        GL = (struct Group_List * )malloc(sizeof(struct Group_List));
        memset(GL, 0, sizeof(struct Group_List));
        snprintf(buf, sizeof(buf), "Categories / %s", header);
        // Install and Uninstall
        for (i = 0; i < NUMBER_BUTTOM_LIST; i += 2) {
            GL->page[i] = scroll_page_new();
            scroll_page_set_title(GL->page[i], buf);
            GL->list[i] =  scroll_list_new(GL->page[i]);
        }
        // Update page
        if (!__update_sigle) {
            __update_sigle = scroll_page_new();
            snprintf(buf, sizeof(buf), "Update Packages");
            scroll_page_set_title(__update_sigle, buf);
            __update_list = scroll_list_new(__update_sigle);
        }
        GL->list[UPDATE] = __update_list;
        GL->page[UPDATE] =  __update_sigle;

        iter = (struct search_iter *)calloc(1, sizeof(struct search_iter));
        iter->group = group;
        iter->GL = GL;
        __search_add(iter);
    }
    return GL;
}


inline int get_index_in_categories_tree(Evas_List *list, Etk_Tree_Row *row) {
    int i = 0;
    for (i = 0;i < evas_list_count(list);i++) {
        if (((struct Group_List *)evas_list_nth(list, i))->row == row) {
            return i;
        }
    }
    return -1;
}
static Etk_Bool categories_selected(Etk_Object *object, Etk_Tree_Row *row, Etk_Event_Mouse_Up *event, void *data) {
    struct Group_List *GL;
    int action_type = get_button_bar_selected_type();
    Evas_List *gl_list = (Evas_List *) data;
    debug("clicked !! list count=%d\n", evas_list_count(gl_list));
    debug("The %d'th item is selected!!\n", get_index_in_categories_tree(gl_list, row));
    GL = evas_list_nth(gl_list, get_index_in_categories_tree(gl_list, row));

    scroll_page_shows(GL->page[action_type]);
    put_a_mask_on_screen();

    button_bar_back_arrow_show(1);

    return ETK_TRUE;
}

inline int get_index_in_packages_tree(Evas_List *list, Etk_Tree_Row *row) {
    int i = 0;
    for (i = 0;i < evas_list_count(list);i++) {
        if (((struct PKG_Item *)evas_list_nth(list, i))->row == row) {
            return i;
        }
    }
    return -1;
}

struct __DELAY_GET_DETAIL {
    struct PKG_Item * pkg_item;
    Pk_Operation * pko;
} _delay_detail;

static int _delayed_get_pk_detail(void *data) {
    struct __DELAY_GET_DETAIL *dgd = (struct __DELAY_GET_DETAIL *)data;
    pk_get_details (dgd->pko, dgd->pkg_item->packageid);
    return 0;
}

static Etk_Bool packages_selected(Etk_Object *object, Etk_Tree_Row *row, Etk_Event_Mouse_Up *event, void *data) {
    Evas_List *list = (Evas_List *)data;
    struct PKG_Item * pkg_item = NULL;
    Pk_Operation * pko = NULL;

    debug("The %d'th item is selected!!\n", get_index_in_packages_tree(list, row));
    pkg_item = (struct PKG_Item * )evas_list_nth(list, get_index_in_packages_tree(list, row));
    debug("packageid = %s\n", pkg_item->packageid);

    if (!strcmp(pkg_item->packageid, "ASSASSIN_END_OF_LIST")) {
        return ETK_TRUE;
    }

    pko = pko_new();
    pko->payload = NULL;

    pko->Operations[Details_sig] = & pk_get_details_cb;

    _delay_detail.pkg_item = pkg_item;
    _delay_detail.pko = pko;

    ecore_timer_add(0.266, &_delayed_get_pk_detail, &_delay_detail);  // MAGIC NUMBER FROM WILL

    put_a_mask_on_screen();
    button_bar_back_arrow_show(1);
    return ETK_TRUE;
}


void pkg_item_free(struct PKG_Item *item) {
    if (!item) {
        error("item is NULL\n");
        return;
    }
    free(item->packageid);
    free(item);
}
